package query

import (
	"context"
	"fmt"
	"math"
	"src/create"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

// 最基本的查询，一个查询条件即可，参考：https://www.mongodb.com/docs/manual/tutorial/query-documents/
func Find(mongo *mongo.Database, ctx context.Context) {
	baseAdd := create.BaseAdd{}

	filter := bson.M{} // 空结构体匹配所有
	// 查询数据，并解析成结构体
	err := mongo.Collection("test").FindOne(ctx, filter).Decode(&baseAdd)
	if err != nil {
		fmt.Println("err")
	}

	fmt.Println(baseAdd)
}

// 用$in关键字查询有多个值的情况
func FindIn(mongo *mongo.Database, ctx context.Context) {
	filter := bson.M{"int32": bson.M{"$in": bson.A{math.MaxInt32, math.MaxInt32 - 1}}}
	cur, err := mongo.Collection("test").Find(ctx, filter)
	if err != nil {
		fmt.Println("err")
	}
	// 多个文档解析程数组的方法
	var baseAdd []create.BaseAdd
	err = cur.All(ctx, &baseAdd)
	fmt.Println(err)
	fmt.Println(baseAdd)
}

// 用gt和lt关键字查询大于和小于，后面加个e表示等于
func FindGteAndLte(mongo *mongo.Database, ctx context.Context) {
	// 多个条件是隐式的and关系，表示要同时满足全部条件
	filter := bson.M{"int32": bson.M{"$gte": math.MaxInt32 - 1, "$lt": math.MaxInt32}}
	cur, err := mongo.Collection("test").Find(ctx, filter)
	if err != nil {
		fmt.Println("err")
	}
	// 遍历数据
	for cur.TryNext(ctx) {
		result, _ := cur.Current.Elements()
		fmt.Println(result)
	}
	cur.Close(ctx)
}

// 用or关键字表示多个条件只需满足其一
func FindOr(mongo *mongo.Database, ctx context.Context) {
	// or对应的是个数组，只要满足数组里任一条件都可
	filter := bson.M{"$or": bson.A{bson.M{"int32": bson.M{"$gte": math.MaxInt32 - 1}}, bson.M{"int32": bson.M{"$lt": math.MaxInt32}}}}
	cur, err := mongo.Collection("test").Find(ctx, filter)
	if err != nil {
		fmt.Println("err")
	}
	// 遍历数据
	for cur.TryNext(ctx) {
		result, _ := cur.Current.Elements()
		fmt.Println(result)
	}
	cur.Close(ctx)
}

// 以字段的子字段为条件查询
func FindChild(mongo *mongo.Database, ctx context.Context) {

	baseAdd := create.BaseAdd{}
	filter := bson.M{"object.id": 2} // 直接用点连接就好，没什么区别
	err := mongo.Collection("test").FindOne(ctx, filter).Decode(&baseAdd)
	if err != nil {
		fmt.Println("err")
	}
	fmt.Println(baseAdd)
}

// 之前的查询我们都是返回所有字段，还可以选择查询指定字段
func FindSelectField(mongo *mongo.Database, ctx context.Context) {

	filter := bson.M{} // 空结构体匹配所有
	// 1表示只查询id字段，但是，主键_id会默认查询出来
	options := options.FindOptions{Projection: bson.M{"id": 1}}
	// options := options.FindOptions{Projection: bson.M{"id": 1, "_id": 0}} // 所以还可以加上"_id": 0排除主键

	cur, err := mongo.Collection("test").Find(ctx, filter, &options)
	if err != nil {
		fmt.Println("err")
	}
	// 遍历数据
	for cur.TryNext(ctx) {
		result, _ := cur.Current.Elements()
		fmt.Println(result)
	}
	cur.Close(ctx)
}

// 与上面相反，可以指定一个字段不要，查询剩余字段
func FindEliminate(mongo *mongo.Database, ctx context.Context) {
	filter := bson.M{} // 空结构体匹配所有
	// 排除_id，其他字段都会被查出来，可以排除多个
	options := options.FindOptions{Projection: bson.M{"_id": 0}}
	cur, err := mongo.Collection("test").Find(ctx, filter, &options)
	if err != nil {
		fmt.Println("err")
	}
	// 遍历数据
	for cur.TryNext(ctx) {
		result, _ := cur.Current.Elements()
		fmt.Println(result)
	}
	cur.Close(ctx)
}

// 对于嵌套的文档也同样受用
func FindChildField(mongo *mongo.Database, ctx context.Context) {
	filter := bson.M{} // 空结构体匹配所有
	// 嵌套字段也一样这么查
	options := options.FindOptions{Projection: bson.M{"object.id": 1, "_id": 0}}

	cur, err := mongo.Collection("test").Find(ctx, filter, &options)
	if err != nil {
		fmt.Println("err")
	}
	// 遍历数据
	for cur.TryNext(ctx) {
		result, _ := cur.Current.LookupErr("object")
		fmt.Println(result)
	}
	cur.Close(ctx)
}
